// Copyright 2011 George Cook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#import "NSString+Additions.h"


@implementation NSString (Additions)

- (NSString *) escape
{
	NSString *escaped =(NSString *)CFURLCreateStringByAddingPercentEscapes(
			NULL,
			(CFStringRef)self,
			NULL,
			(CFStringRef)@"!*'();:@&=+$,/?%#[]",
			kCFStringEncodingUTF8 );
	return [escaped autorelease];
}

- (NSString *) stringByDecodingXMLEntities
{
	NSUInteger myLength = [self length];
	NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;

	// Short-circuit if there are no ampersands.
	if (ampIndex == NSNotFound) {
		return self;
	}
	// Make result string with some extra capacity.
	NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];

	// First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
	NSScanner *scanner = [NSScanner scannerWithString:self];
	do {
		// Scan up to the next entity or the end of the string.
		NSString *nonEntityString;
		if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
			[result appendString:nonEntityString];
		}
		if ([scanner isAtEnd]) {
			goto finish;
		}
		// Scan either a HTML or numeric character entity reference.
		if ([scanner scanString:@"&amp;" intoString:NULL])
			[result appendString:@"&"];
		else if ([scanner scanString:@"&apos;" intoString:NULL])
			[result appendString:@"'"];
		else if ([scanner scanString:@"&quot;" intoString:NULL])
			[result appendString:@"\""];
		else if ([scanner scanString:@"&lt;" intoString:NULL])
			[result appendString:@"<"];
		else if ([scanner scanString:@"&gt;" intoString:NULL])
			[result appendString:@">"];
		else if ([scanner scanString:@"&#" intoString:NULL])
		{
			BOOL gotNumber;
			unsigned charCode;
			NSString *xForHex = @"";

			// Is it hex or decimal?
			if ([scanner scanString:@"x" intoString:&xForHex]) {
				gotNumber = [scanner scanHexInt:&charCode];
			}
			else {
				gotNumber = [scanner scanInt:(int*)&charCode];
			}
			if (gotNumber) {
				[result appendFormat:@"%C", charCode];
			}
			else {
				NSString *unknownEntity = @"";
				[scanner scanUpToString:@";" intoString:&unknownEntity];
				[result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
				NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);
			}
			[scanner scanString:@";" intoString:NULL];
		}
		else {
			NSString *unknownEntity = @"";
			[scanner scanUpToString:@";" intoString:&unknownEntity];
			NSString *semicolon = @"";
			[scanner scanString:@";" intoString:&semicolon];
			[result appendFormat:@"%@%@", unknownEntity, semicolon];
			NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
		}
	}
	while (![scanner isAtEnd]);

finish:
	return result;
}


+ (NSString *)uuid
{
	CFUUIDRef uuidRef = CFUUIDCreate(NULL);
	CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
	CFRelease(uuidRef);
	return [(NSString *) uuidStringRef autorelease];
}
@end
